// 源于Inferno utils/6c/txt.c
// https:
// None
// None
// None
// None
// None
// None
// None
// None
// None
// None
// 特此向任何获得副本的人免费授予许可
// 本软件和相关文档文件（“软件”）的
// 在软件中不受限制，包括但不限于权利
// 使用、复制、修改、合并、发布、分发、再许可和/或销售
// 软件的副本，并允许向其提供软件的人员
// 按照以下条件提供：
// None
// 上述版权声明和本许可声明应包含在
// 软件的所有副本或主要部分。
// None
// 本软件按“原样”提供，无任何形式的明示或明示担保
// 默示，包括但不限于适销性保证，
// 适用于特定目的和非侵权。在任何情况下
// 作者或版权持有人应承担任何索赔、损害或其他责任
// 无论是在合同诉讼、侵权诉讼或其他诉讼中，由以下原因引起的责任：，
// 与本软件有关或与本软件的使用或其他交易有关
// 软件。

package objw

import (
	"cmd/compile/internal/base"
	"cmd/compile/internal/ir"
	"cmd/internal/obj"
	"cmd/internal/objabi"
	"cmd/internal/src"
)

var sharedProgArray = new([10000]obj.Prog) // *T代替T解决问题19839

// NewProgs为fn返回一个新的Progs。
// worker指示哪个后端worker将使用Progs。
func NewProgs(fn *ir.Func, worker int) *Progs {
	pp := new(Progs)
	if base.Ctxt.CanReuseProgs() {
		sz := len(sharedProgArray) / base.Flag.LowerC
		pp.Cache = sharedProgArray[sz*worker : sz*(worker+1)]
	}
	pp.CurFunc = fn

	// 给泵充油
	pp.Next = pp.NewProg()
	pp.Clear(pp.Next)

	pp.Pos = fn.Pos()
	pp.SetText(fn)
	// PCDATA表隐式地以索引-1开头。
	pp.PrevLive = LivenessIndex{-1, false}
	pp.NextLive = pp.PrevLive
	return pp
}

// Progs累积函数的Progs并将其转换为机器代码。
type Progs struct {
	Text       *obj.Prog  // 此函数的ATEXT Prog
	Next       *obj.Prog  // 下一步
	PC         int64      // 虚拟PC；程序计数
	Pos        src.XPos   // 用于新程序的位置
	CurFunc    *ir.Func   // fn这些程序是为
	Cache      []obj.Prog // 本地程序缓存
	CacheIndex int        // progcache的第一个空闲元素

	NextLive LivenessIndex // 下一个程序的活跃度指数
	PrevLive LivenessIndex // 最后发射活性指数
}

// LivenessIndex存储值的活性映射信息。
type LivenessIndex struct {
	StackMapIndex int

	// IsUnsafePoint表示这是一个不安全点。
	// None
	// 请注意，调用值可能具有堆栈
	// 地图同时也是一个不安全的点。这意味着它不能
	// 在本指令下被抢占，但该抢占或
	// 堆栈增长可能发生在被调用的函数中。
	IsUnsafePoint bool
}

// StackMapDontCare表示堆栈映射索引的值为
// 没关系。
// None
// 这是一个不应发送到PCDATA的sentinel值
// 流动我们使用-1000，因为这显然不是有效的堆栈
// 索引（但-1是）。
const StackMapDontCare = -1000

// LivenessDontCare表示liveness信息不存在
// 课题目前，当我们不使用它时，它被用于返回活力
// 实际上我需要它。不应将其发送到PCDATA流。
var LivenessDontCare = LivenessIndex{StackMapDontCare, true}

func (idx LivenessIndex) StackMapValid() bool {
	return idx.StackMapIndex != StackMapDontCare
}

func (pp *Progs) NewProg() *obj.Prog {
	var p *obj.Prog
	if pp.CacheIndex < len(pp.Cache) {
		p = &pp.Cache[pp.CacheIndex]
		pp.CacheIndex++
	} else {
		p = new(obj.Prog)
	}
	p.Ctxt = base.Ctxt
	return p
}

// Flush将pp转换为机器代码。
func (pp *Progs) Flush() {
	plist := &obj.Plist{Firstpc: pp.Text, Curfn: pp.CurFunc}
	obj.Flushplist(base.Ctxt, plist, pp.NewProg, base.Ctxt.Pkgpath)
}

// 免费清除pp和任何相关资源。
func (pp *Progs) Free() {
	if base.Ctxt.CanReuseProgs() {
		// 清除程序以启用GC并避免滥用。
		s := pp.Cache[:pp.CacheIndex]
		for i := range s {
			s[i] = obj.Prog{}
		}
	}
	// 清除pp以避免滥用。
	*pp = Progs{}
}

// Prog添加一个Prog，其中包含关于pp的指令。
func (pp *Progs) Prog(as obj.As) *obj.Prog {
	if pp.NextLive.StackMapValid() && pp.NextLive.StackMapIndex != pp.PrevLive.StackMapIndex {
		// 发出堆栈映射索引更改。
		idx := pp.NextLive.StackMapIndex
		pp.PrevLive.StackMapIndex = idx
		p := pp.Prog(obj.APCDATA)
		p.From.SetConst(objabi.PCDATA_StackMapIndex)
		p.To.SetConst(int64(idx))
	}
	if pp.NextLive.IsUnsafePoint != pp.PrevLive.IsUnsafePoint {
		// 发出不安全的点标记。
		pp.PrevLive.IsUnsafePoint = pp.NextLive.IsUnsafePoint
		p := pp.Prog(obj.APCDATA)
		p.From.SetConst(objabi.PCDATA_UnsafePoint)
		if pp.NextLive.IsUnsafePoint {
			p.To.SetConst(objabi.PCDATA_UnsafePointUnsafe)
		} else {
			p.To.SetConst(objabi.PCDATA_UnsafePointSafe)
		}
	}

	p := pp.Next
	pp.Next = pp.NewProg()
	pp.Clear(pp.Next)
	p.Link = pp.Next

	if !pp.Pos.IsKnown() && base.Flag.K != 0 {
		base.Warn("prog: unknown position (line 0)")
	}

	p.As = as
	p.Pos = pp.Pos
	if pp.Pos.IsStmt() == src.PosIsStmt {
		// 如果as可以标记为stmt，则清除IsStmt以用于此pos的后续程序
		if LosesStmtMark(as) {
			return p
		}
		pp.Pos = pp.Pos.WithNotStmt()
	}
	return p
}

func (pp *Progs) Clear(p *obj.Prog) {
	obj.Nopout(p)
	p.As = obj.AEND
	p.Pc = pp.PC
	pp.PC++
}

func (pp *Progs) Append(p *obj.Prog, as obj.As, ftype obj.AddrType, freg int16, foffset int64, ttype obj.AddrType, treg int16, toffset int64) *obj.Prog {
	q := pp.NewProg()
	pp.Clear(q)
	q.As = as
	q.Pos = p.Pos
	q.From.Type = ftype
	q.From.Reg = freg
	q.From.Offset = foffset
	q.To.Type = ttype
	q.To.Reg = treg
	q.To.Offset = toffset
	q.Link = p.Link
	p.Link = q
	return q
}

func (pp *Progs) SetText(fn *ir.Func) {
	if pp.Text != nil {
		base.Fatalf("Progs.SetText called twice")
	}
	ptxt := pp.Prog(obj.ATEXT)
	pp.Text = ptxt

	fn.LSym.Func().Text = ptxt
	ptxt.From.Type = obj.TYPE_MEM
	ptxt.From.Name = obj.NAME_EXTERN
	ptxt.From.Sym = fn.LSym
}

// LosesStmtMark报告带有op as的prog是否在到达DWARF的过程中丢失其语句标记。
// 某些操作码的属性在转换过程中丢失。
// TODO:这是funcpctab如何在单个PC上组合指令信息的一个产物。
// 你应该试着把它修好。
func LosesStmtMark(as obj.As) bool {
	// is_stmt不适用于这些；即使ANOP不生成代码，它也会为ANOP生成代码。
	return as == obj.APCDATA || as == obj.AFUNCDATA
}
